package com.yunxin.admin.system.api.system;

import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yunxin.admin.system.application.LogApplicationService;
import com.yunxin.admin.system.application.UserQueryService;
import com.yunxin.admin.system.application.command.LogCommand;
import com.yunxin.admin.system.application.dto.CaptchaDTO;
import com.yunxin.admin.system.application.dto.LoginDTO;
import com.yunxin.admin.system.infra.common.constant.SecurityConstants;
import com.yunxin.admin.system.infra.common.util.RequestUtils;
import com.yunxin.admin.system.infra.framework.easycaptcha.service.EasyCaptchaService;
import com.yunxin.admin.system.infra.framework.security.JwtTokenManager;
import com.yunxin.admin.system.infra.framework.security.SmsAuthenticationToken;
import com.yunxin.admin.system.infra.framework.security.userdetails.SysUserDetails;
import com.yunxin.admin.system.infra.framework.security.util.SecurityUtils;
import com.yunxin.common.exception.BusinessException;
import com.yunxin.common.result.Result;
import com.yunxin.common.result.ResultCode;
import com.yunxin.core.admin.domain.AdminUser;
import com.yunxin.core.admin.service.AdminUserService;
import com.yunxin.core.admin.service.AdminUserSonService;
import com.yunxin.core.application.service.ContactApplicationGatewayService;
import com.yunxin.core.customer.domain.CustomInfoAuthorizationLog;
import com.yunxin.core.customer.service.RiskManagementService;
import com.yunxin.core.infra.common.contact.enums.PassiveTriggerEnum;
import com.yunxin.core.utils.infra.utils.IpUtils;
import com.yunxin.core.wechat.domain.WechatInfo;
import com.yunxin.core.wechat.service.QyWxService;
import com.yunxin.core.wechat.service.WechatInfoService;
import com.yunxin.core.wechat.service.WechatUserSysService;
import com.yunxin.core.wechat.vo.LoginVo;
import io.jsonwebtoken.Claims;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;

import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author zero°
 * @since 2023-05-22
 */
@Tag(name = "01.认证中心")
@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
@Slf4j
public class AuthController {
    private final AuthenticationManager authenticationManager;

    private final JwtTokenManager jwtTokenManager;

    private final EasyCaptchaService easyCaptchaService;

    private final RedisTemplate redisTemplate;

    private final UserQueryService userQueryService;

    private final LogApplicationService logApplicationService;

    private final ContactApplicationGatewayService contactApplicationGatewayService;

    @Autowired
    private QyWxService qyWxService;

    @Autowired
    private WechatUserSysService wechatUserSysService;

    @Autowired
    private AdminUserService adminUserService;

    @Autowired
    private RiskManagementService riskManagementService;

    @Autowired
    private WechatInfoService wechatInfoService;

    @Autowired
    private AdminUserSonService adminUserSonService;

    @Operation(summary = "短信验证码登录")
    @PostMapping("/smsLogin")
    public Result<LoginDTO> smsLogin(HttpServletRequest request,
                                     @Parameter(description = "手机号") @RequestParam(name = "phone") String phone,
                                     @Parameter(description = "短信验证码") @RequestParam(name = "verifyCode") String verifyCode,
                                     @Parameter(description = "场景,默认值为adminLogin") @RequestParam(name = "scene", defaultValue = "adminLogin") String scene) {
        contactApplicationGatewayService.verificationSmsCode(phone, verifyCode, scene);
        SmsAuthenticationToken authenticationToken = new SmsAuthenticationToken(phone.toLowerCase().trim());
        Authentication authentication = authenticationManager.authenticate(authenticationToken);
        // 生成token
        String accessToken = jwtTokenManager.createToken(authentication);
        LoginDTO loginDTO = LoginDTO.builder()
                .tokenType("Bearer")
                .accessToken(accessToken)
                .build();
        saveSysLog(phone, "用户短信验证码登录成功,phone:" + phone, 1, "com.yunxin.admin.system.api.system.AuthController.sysLogin()", request);
        return Result.success(loginDTO);
    }

    @Operation(summary = "获取短信验证码")
    @PostMapping("/getSmsCaptcha")
    public Result<String> getSmsCaptcha(HttpServletRequest request,
                                        @Parameter(description = "手机号", required = true) @RequestParam(name = "phone") String phone,
                                        @Parameter(description = "场景,默认值为adminLogin") @RequestParam(name = "scene", defaultValue = "adminLogin") String scene) {
        Long userId = userQueryService.getUserLoginInfoByPhone(phone);
        if (Objects.isNull(userId)) {
            return Result.failed(ResultCode.USER_NOT_EXIST);
        }
        String clientIp = IpUtils.X.getIpAddr(request);
        contactApplicationGatewayService.sendSmsVerificationCode(PassiveTriggerEnum.ADMIN_LOGIN_SMS.getTaskCode(), phone, scene, clientIp);
        saveSysLog(phone, "用户发送短信验证码成功,phone:" + phone, 1, "com.yunxin.admin.system.api.system.AuthController.getSmsCaptcha()", request);
        return Result.success();
    }

    @Operation(summary = "登录")
    @PostMapping("/login")
    public Result<LoginDTO> login(HttpServletRequest request, @Parameter(description = "用户名") @RequestParam String username,
                                  @Parameter(description = "密码") @RequestParam String password) {
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                username.toLowerCase().trim(),
                password
        );
        Authentication authentication = authenticationManager.authenticate(authenticationToken);
        // 生成token
        String accessToken = jwtTokenManager.createToken(authentication);
        LoginDTO loginDTO = LoginDTO.builder()
                .tokenType("Bearer")
                .accessToken(accessToken)
                .build();
        saveSysLog(username, "用户登录成功,username:" + username, 1, "com.yunxin.admin.system.application.controller.system.AuthController.login()" ,request);
        return Result.success(loginDTO);
    }

    @Operation(summary = "企业微信登录")
    @PostMapping("/wechatLogin")
    public Result<LoginDTO> wechatLogin(HttpServletRequest request,
                                        @Parameter(description = "微信授权code") @RequestParam String code,
                                        @Parameter(description = "微信授权state") @RequestParam String state) {
        LambdaQueryWrapper<WechatInfo> wechatInfoLambdaQueryWrapper=new LambdaQueryWrapper<>();
        wechatInfoLambdaQueryWrapper.eq(WechatInfo::getCorpid,state);
        WechatInfo wechatInfo = wechatInfoService.getOne(wechatInfoLambdaQueryWrapper);
        if (ObjectUtils.isEmpty(wechatInfo)){
            throw new BusinessException("状态码无效");
        }
        LoginVo loginVo = qyWxService.getuserinfo(code,wechatInfo);
        if (ObjectUtils.isEmpty(loginVo)){
            throw new BusinessException("code无效");
        }
        AdminUser adminUser = wechatUserSysService.getAdminUser(loginVo);
        if (ObjectUtils.isEmpty(adminUser)){
            throw new BusinessException("无权登录,请联系管理员");
        }
        SmsAuthenticationToken authenticationToken = new SmsAuthenticationToken(adminUser.getMobile().toLowerCase().trim());
        Authentication authentication = authenticationManager.authenticate(authenticationToken);
        // 生成token
        String accessToken = jwtTokenManager.createToken(authentication);
        LoginDTO loginDTO = LoginDTO.builder()
                .tokenType("Bearer")
                .accessToken(accessToken)
                .build();
        saveSysLog(adminUser.getUsername(), "用户微信code码登录成功,code:" + code, 1, "com.yunxin.admin.system.api.system.AuthController.sysLogin()", request);
        return Result.success(loginDTO);
    }

    @Operation(summary = "切换子用户账号", security = {@SecurityRequirement(name = SecurityConstants.TOKEN_KEY)})
    @GetMapping("/cutUserByUserId")
    public Result cutUserByUserId(HttpServletRequest request,
                                  @Parameter(description = "用户id") @RequestParam Integer id) {
        int userId = SecurityUtils.X.getUser().getUserId().intValue();
        AdminUser adminUser = adminUserSonService.cutUserByUserId(userId, id);
        if (ObjectUtils.isEmpty(adminUser)){
            throw new BusinessException("无权切换,请联系管理员");
        }
        SmsAuthenticationToken authenticationToken = new SmsAuthenticationToken(adminUser.getMobile().toLowerCase().trim());
        Authentication authentication = authenticationManager.authenticate(authenticationToken);
        // 生成token
        String accessToken = jwtTokenManager.createToken(authentication);
        LoginDTO loginDTO = LoginDTO.builder()
                .tokenType("Bearer")
                .accessToken(accessToken)
                .build();
        saveSysLog(adminUser.getUsername(), "用户切换子用户登录成功,username:" + adminUser.getUsername(), 1, "com.yunxin.admin.system.api.system.AuthController.sysLogin()", request);
        return Result.success(loginDTO);
    }



    @Operation(summary = "注销", security = {@SecurityRequirement(name = SecurityConstants.TOKEN_KEY)})
    @DeleteMapping("/logout")
    public Result<String> logout(HttpServletRequest request) {
        String token = RequestUtils.resolveToken(request);
        if (CharSequenceUtil.isNotBlank(token)) {
            Claims claims = jwtTokenManager.getTokenClaims(token);
            String jti = claims.get("jti", String.class);
            Date expiration = claims.getExpiration();
            if (expiration != null) {
                // 有过期时间，在token有效时间内存入黑名单，超出时间移除黑名单节省内存占用
                long ttl = (expiration.getTime() - System.currentTimeMillis());
                redisTemplate.opsForValue().set(SecurityConstants.BLACK_TOKEN_CACHE_PREFIX + jti, null, ttl, TimeUnit.MILLISECONDS);
            } else {
                // 无过期时间，永久加入黑名单
                redisTemplate.opsForValue().set(SecurityConstants.BLACK_TOKEN_CACHE_PREFIX + jti, null);
            }
        }
        SysUserDetails sysUserDetails = SecurityUtils.X.getUser();
        String username = sysUserDetails.getUsername();
        saveSysLog(username,"用户退出成功,username:" + username, 1, "com.yunxin.admin.system.application.controller.system.AuthController.logout()" ,request);
        SecurityContextHolder.clearContext();
        return Result.success("注销成功");
    }

    @Operation(summary = "获取验证码")
    @GetMapping("/captcha")
    public Result<CaptchaDTO> getCaptcha() {
        CaptchaDTO captcha = easyCaptchaService.getCaptcha();
        return Result.success(captcha);
    }

    @Operation(summary = "获取风控协议信息", security = {@SecurityRequirement(name = SecurityConstants.TOKEN_KEY)})
    @PostMapping("/getLogById")
    public Result<CustomInfoAuthorizationLog> getLogById(@RequestParam(value = "logId")Long logId) {
        CustomInfoAuthorizationLog log = riskManagementService.getLogByid(logId);
        return Result.success(log);
    }

    /**
     * 保存系统日志
     * @param userName userName
     * @param logContent logContent
     * @param logType logType
     * @param methodName methodName
     * @param request request
     */
    private void saveSysLog(String userName, String logContent, Integer logType, String methodName, HttpServletRequest request){
        LogCommand logCommand = new LogCommand();
        //注解上的描述,操作日志内容
        logCommand.setLogContent(logContent);
        logCommand.setLogType(logType);
        //请求的方法名
        logCommand.setMethod(methodName);
        //设置请求地址
        logCommand.setRequestUrl(request.getRequestURL().toString());
        //设置IP地址
        logCommand.setIp(IpUtils.X.getIpAddr(request));
        logCommand.setRequestType(request.getMethod());
        logCommand.setCreator(userName);
        logCommand.setCreateTime(new Date());
        //保存系统日志
        logApplicationService.saveLog(logCommand);
    }

}
